package juegocartas.mus;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Fase del juego que corresponde a un lance (grandes, chicas, pares, juego o punto)
 * El lance se representa con un objeto tipo Lance.
 * @author jorge
 */
public class FaseLance implements Fase {

    protected MesaMus mesa;
    protected Lance lance;
    protected int puntosEnJuego;
    protected int puntosEnvite;
    private final Set<JugadorMus> jugadoresPorResponder = new HashSet<JugadorMus>();
    private final List<JugadorMus> jugadoresPorHablar = new ArrayList<JugadorMus>();
    private final List<JugadorMus> jugadoresParticipantes = new ArrayList<JugadorMus>();
    private JugadorMus jugadorQueEnvida;
    private JugadorMus jugadorQueHabla;
    private JugadorMus jugadorGanador;
    private static final int ESTADO_QUIEN_APUESTA = 1;
    private static final int ESTADO_ENVITE = 2;
    private static final int ESTADO_ORDAGO = 3;
    private int estado;

    public FaseLance(MesaMus motor, Lance comparador) {
        this.mesa = motor;
        this.lance = comparador;
    }

    @Override
    public String toString() {
        return getNombre();
    }

    public void iniciar() {
        verificarQuienParticipa();

        if (jugadoresParticipantes.isEmpty()) {
            mesa.nadieTieneCartasParaJugarLance();
            return;
        } else if (!patricipanDosEquipos()) {
            mesa.iniciarSiguienteLance();
            return;
        }
        estado = ESTADO_QUIEN_APUESTA;
        jugadoresPorHablar.addAll(jugadoresParticipantes);
        preguntarSiApuestaSiguienteJugador();
    }

    private void verificarQuienParticipa() {

        if (!lance.esParOJuego()) {
            jugadoresParticipantes.addAll(mesa.getJugadores());
            return;
        }
        for (JugadorMus jugador : mesa.getJugadores()) {
            boolean tiene = lance.tieneCartasParaJugar(jugador.getCartas());
            if (tiene) {
                jugadoresParticipantes.add(jugador);
            }
            informarTiene(jugador, tiene);
        }
    }

    private boolean patricipanDosEquipos() {
        if (jugadoresParticipantes.isEmpty()) {
            return false;
        }
        Equipo equipo = jugadoresParticipantes.get(0).getEquipo();

        for (JugadorMus jugador : jugadoresParticipantes) {
            if (equipo != jugador.getEquipo()) {
                return true;
            }
        }
        return false;
    }

    private void preguntarSiApuestaSiguienteJugador() {
        if (jugadoresPorHablar.isEmpty()) {
            informarSeFue();
            mesa.iniciarSiguienteLance();
            return;
        }

        jugadorQueHabla = jugadoresPorHablar.get(0);
        jugadoresPorHablar.remove(0);

        jugadorQueHabla.decidirSiApuesta();
    }

    public void jugadorDiceMus(JugadorMus jugador) {
        mesa.error(jugador, "El jugador no puede dar mus ahora");
    }

    public void jugadorDiceCorto(JugadorMus jugador) {
        mesa.error(jugador, "El jugador no puede cortar ahora");
    }

    public void jugadorDicePaso(JugadorMus jugador) {

        if (estado != ESTADO_QUIEN_APUESTA) {
            mesa.error(jugador, "El jugador no puede pasar en este punto");
            return;
        }
        if (jugadorQueHabla != jugador) {
            mesa.error(jugador, "Al jugador  no le toca hablar");
            return;
        }

        informarPasa(jugador);

        preguntarSiApuestaSiguienteJugador();
    }

    public void jugadorDiceEnvido(JugadorMus jugador, int puntos) {
        informarEnvida(jugador, puntos);

        puntosEnvite = puntos;
        jugadorQueEnvida = jugador;
        jugadoresPorResponder.addAll(mesa.getJugadoresContrarios(jugador));

        for (JugadorMus jugadorPorResponder : jugadoresPorResponder) {
            jugadorPorResponder.decidirSiVeApuesta();
        }
    }

    public void jugadorDiceOrdago(JugadorMus jugador) {
        if (estado == ESTADO_ORDAGO) {
            mesa.error(jugador, "No puede echar un ordago despues de otro");
            return;
        }

        informarOrdago(jugador);

        estado = ESTADO_ORDAGO;
        jugadoresPorResponder.addAll(mesa.getJugadoresContrarios(jugador));
        for (JugadorMus jugadorPorResponder : jugadoresPorResponder) {
            jugadorPorResponder.decidirSiVeOrdago();
        }
    }

    public void jugadorDiceQuiero(JugadorMus jugador) {
        if (!jugadoresPorResponder.contains(jugador)) {
            mesa.error(jugador, "El jugador no puede responder ahora");
            return;
        }

        jugadorGanador = evaluarJugadorGanador();

        if (estado == ESTADO_ORDAGO) {
            informarGanadorMano(jugadorGanador);
            return;
        }
        puntosEnJuego += puntosEnvite;
        jugadorGanador.sumarPuntos(puntosEnJuego);
        mesa.iniciarSiguienteLance();
    }

    public void jugadorDiceNoQuiero(JugadorMus jugador) {

        if (estado != ESTADO_ENVITE) {
            mesa.error(jugador, "Al jugador no puede decir que no quiere ahora");
            return;
        }

        informarNoQuiere(jugador);

        jugadoresPorResponder.remove(jugador);
        if (jugadoresPorResponder.isEmpty()) {
            jugadorGanador = jugadorQueEnvida;
            jugadorGanador.sumarPuntos(puntosEnJuego);
            mesa.iniciarSiguienteLance();
        }
    }

    public void jugadorDescarta(JugadorMus jugador, List<Carta> descarte) {
        mesa.error(jugador, "El jugador no puede descartar ahora");
    }

    private JugadorMus evaluarJugadorGanador() {
        AlgoritmoComparacion algoritmo = new AlgoritmoComparacion(lance);
        return algoritmo.resolverGanador(mesa.getJugadores());
    }

    public String getNombre() {
        return "Lance de " + lance.getNombre();
    }

    public void contabilizarPuntos() {
        boolean sePaso = jugadorGanador == null;
        if (sePaso) {
            jugadorGanador = evaluarJugadorGanador();
        }

        Equipo equipoGanador = jugadorGanador.getEquipo();
        int puntosSumados = lance.contabilizadPuntosSumados(equipoGanador, sePaso);
        
        if (puntosSumados > 0) {
            equipoGanador.sumarPuntos(puntosSumados);
            informarEquipoSumaPuntos(equipoGanador, puntosSumados, lance.getNombre());
        }
    }
    
    private void informarEquipoSumaPuntos(Equipo equipo, int puntos, String nombreLance) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarEquipoSumaPuntos(equipo, puntos, nombreLance);
        }
    }

    private void informarGanadorMano(JugadorMus jugadorGanador) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarGanadorMano(jugadorGanador);
        }
    }

    private void informarTiene(JugadorMus jugador, boolean tiene) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarTiene(jugador, tiene);
        }
    }

    private void informarSeFue() {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarSeFue();
        }
    }

    private void informarPasa(JugadorMus jugador) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarPasa(jugador);
        }
    }

    private void informarEnvida(JugadorMus jugador, int puntos) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarEnvida(jugador, puntos);
        }
    }

    private void informarOrdago(JugadorMus jugador) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarOrdago(jugador);
        }
    }

    private void informarNoQuiere(JugadorMus jugador) {
        for (ObservadorMesaMus observador : mesa.getObservadoresMesa()) {
            observador.informarNoQuiere(jugador);
        }
    }


}
